/* hayes.c */

/********************/
/* HAYES-INTERFACE  */
/* for Secure Phone */
/* (C) 2003 by NAH6 */
/********************/

/*------------*/
/*  INCLUDES  */
/*------------*/

#include <memory.h>

#include "winsio.h"
#include "hayes.h"
#include "wintop.h"

/*-----------*/
/*  DEFINES  */
/*-----------*/

#define K_HAYES_STDINIT_STRINGS    (2)
#define K_HAYES_POTSID_STRINGS     (3)
#define K_HAYES_POTSGAL_STRINGS    (3)
#define K_HAYES_POTSCIR_STRINGS    (4)
#define K_HAYES_POTSELS_STRINGS    (5)
#define K_HAYES_POTSUSR_STRINGS    (5)
#define K_HAYES_POTSRTSCTS_WAYS    (3)
#define K_HAYES_POTSERRCO_WAYS     (4)
#define K_HAYES_GSMINIT_STRINGS   (12)
#define K_HAYES_GSMID_STRINGS      (8)
#define K_HAYES_GSMNOK_STRINGS     (6)

#define K_HAYES_REPLY_STRINGS     (15)
#define K_HAYES_EVENT_STRINGS     (35)
#define K_HAYES_CONMSG1_STRINGS    (4)
#define K_HAYES_CONMSG2_STRINGS    (5)
#define K_HAYES_CONMSG3_STRINGS    (4)
#define K_HAYES_CONMSG4_STRINGS    (5)
#define K_HAYES_REPORTCR_STRINGS  (21)
#define K_HAYES_REPORTDR_STRINGS   (2)

/*--------------*/
/*  STRUCTURES  */
/*--------------*/

struct hayes__modeminfo_basedev
{
  const unsigned char **initstr;
  unsigned char initcnt;
  unsigned char rtscts;
  unsigned char errco;
  unsigned char escape;
  unsigned char atxmd;
  unsigned char conmsg;
  unsigned char tmchar;
  unsigned char tmline;
};

/*--------------*/
/*  PROTOTYPES  */
/*--------------*/

static signed short hayes__hangup  (struct hayes_data *dseg, \
                                    unsigned char check);
static signed short hayes__pollgsm (struct hayes_data *dseg, \
                                    unsigned char dcd, unsigned char ring);

static signed short hayes__askvalues (struct hayes_data *dseg,       \
                                      unsigned char **lenlist,       \
                                      unsigned char **buflist,       \
                                      const unsigned char *maxlens,  \
                                      const unsigned char **cmdlist, \
                                      unsigned char listlen);
static signed short hayes__runbatch  (struct hayes_data *dseg,       \
                                      const unsigned char **cmdlist, \
                                      unsigned char listlen);
static signed short hayes__command (struct hayes_data *dseg,                   \
                                    unsigned char term, unsigned char dcd,     \
                                    unsigned char ring,                        \
                                    unsigned char *timeout,                    \
                                    unsigned char *vlines, unsigned char echo, \
                                    unsigned char reply,                       \
                                    const unsigned char *data);

static signed short hayes__getline (struct hayes_data *dseg,               \
                                    unsigned char term, unsigned char dcd, \
                                    unsigned char ring, unsigned char *timeout);
static signed short hayes__putch (struct hayes_data *dseg, unsigned char term, \
                                  unsigned char dcd, unsigned char ring,       \
                                  unsigned char echo, unsigned char data);

static signed short hayes__readtok   (struct hayes_data *dseg,        \
                                      const unsigned char **textlist, \
                                      unsigned char texts);
static signed short hayes__readstr   (struct hayes_data *dseg, \
                                      unsigned char *text, unsigned char *len);
static signed short hayes__readnum   (struct hayes_data *dseg,                \
                                      unsigned long *val, unsigned char base, \
                                      unsigned char digits);
static signed short hayes__readchar  (struct hayes_data *dseg);
static signed short hayes__writestr  (struct hayes_data *dseg, \
                                      const unsigned char *text);
static signed short hayes__writenum  (struct hayes_data *dseg,               \
                                      unsigned long val, unsigned char base, \
                                      unsigned char digits);
static signed short hayes__writechar (struct hayes_data *dseg, \
                                      unsigned char data);

/*-------------*/
/*  VARIABLEN  */
/*-------------*/

static const unsigned char hayes__command_reset  [2] = "Z";
static const unsigned char hayes__command_hangup [3] = "H0";
static const unsigned char hayes__command_hangsm [6] = "+CHUP";
static const unsigned char hayes__command_geterr1[5] = "S86?";
static const unsigned char hayes__command_getcerr[6] = "+CEER";

static const unsigned char hayes__cmdstr_bq   [3] = "\\Q";
static const unsigned char hayes__cmdstr_cbst1[8] = "+CBST=7";
static const unsigned char hayes__cmdstr_cbst2[5] = ",0,1";
static const unsigned char hayes__cmdstr_ds1  [5] = "+DS=";
static const unsigned char hayes__cmdstr_ds2  [3] = ",0";

/*----*/

static const unsigned char hayes__modemsetup_init1 [7] = \
  "E1Q0V1";
static const unsigned char hayes__modemsetup_init2[26] = \
  "&C1&D2S0=0S2=43S3=13S4=10";
static const unsigned char hayes__modemsetup_init3[26] = \
  "S5=8S7=45S8=2S10=14S12=50";

static const unsigned char hayes__modemsetup_pots1[29] = \
  "B0&G0&S0S6=3S9=6S11=90S25=10";

static const unsigned char hayes__modemsetup_pots_id1[3] = "I0";
static const unsigned char hayes__modemsetup_pots_id2[3] = "I3";
static const unsigned char hayes__modemsetup_pots_id3[3] = "I1";

static const unsigned char hayes__modemsetup_potsgal1[36] = \
  "W1Y0&M0&T5%A13%C0\\A3\\E0\\G0\\J0\\K5\\N0";
static const unsigned char hayes__modemsetup_potsgal2[35] = \
  "\\T5\\V1\\X0*E1*G1*N6*P0*Q1*T1S14=138";
static const unsigned char hayes__modemsetup_potsgal3 [6] = \
  "S27=0";

static const unsigned char hayes__modemsetup_potscir1[37] = \
  "C1F1N1TW3Y0&M0&Q0&U0%A13%C0%E1%G1\\A3";
static const unsigned char hayes__modemsetup_potscir2[35] = \
  "\\C0\\G0\\J0\\K5\\N0\\T5\\X0-C1-J1\"H0\"O32";
static const unsigned char hayes__modemsetup_potscir3[35] = \
  "+MS=V34,1,9600,33600;+VCID=0;S33=0";
static const unsigned char hayes__modemsetup_potscir4 [7] = \
  "S37=19";

static const unsigned char hayes__modemsetup_potsels1[37] = \
  "T%A13%B28800%C0%D0%E1%G1%L1%M0\\C0\\D0";
static const unsigned char hayes__modemsetup_potsels2[35] = \
  "\\J0\\K5\\N0\\T30\\V8\\X0-H0-J1-M0*E0*Q0";
static const unsigned char hayes__modemsetup_potsels3[37] = \
  "$B0$D0$T0:D0S14=138S27=0S28=70S29=99";
static const unsigned char hayes__modemsetup_potsels4[35] = \
  "S31=64S36=0S53=0S54=6S66=61S96=100";
static const unsigned char hayes__modemsetup_potsels5[10] = \
  "S99=70&T5";

static const unsigned char hayes__modemsetup_potsusr1[35] = \
  "C1F1K0&A3&B1&K0&L0&M0&N16&T5&U6&Y3";
static const unsigned char hayes__modemsetup_potsusr2[32] = \
  "S13=4S14=0S15=0S16=0S19=5S21=10";
static const unsigned char hayes__modemsetup_potsusr3[34] = \
  "S22=17S23=19S27=0S28=8S29=20S34=0";
static const unsigned char hayes__modemsetup_potsusr4[32] = \
  "S38=0S41=0S51=0S54=64S55=0S56=0";
static const unsigned char hayes__modemsetup_potsusr5[18] = \
  "S58=33S59=0#CID=0";


static const unsigned char hayes__modemsetup_gsm1  [4] = "TX4";
static const unsigned char hayes__modemsetup_gsm2  [7] = "+CRC=1";
static const unsigned char hayes__modemsetup_gsm3  [8] = "+CMEE=1";
static const unsigned char hayes__modemsetup_gsm4  [6] = "+CR=1";
static const unsigned char hayes__modemsetup_gsm5 [12] = "+CSCS=\"GSM\"";
static const unsigned char hayes__modemsetup_gsm6 [16] = "+CNMI=1,1,2,2,1";
static const unsigned char hayes__modemsetup_gsm7  [8] = "+CMGF=0";
static const unsigned char hayes__modemsetup_gsm8 [14] = "+CSCB=1,\"\",\"\"";
static const unsigned char hayes__modemsetup_gsm9 [13] = "+CBST=71,0,1";
static const unsigned char hayes__modemsetup_gsm10[17] = "+CRLP=61,61,60,6";
static const unsigned char hayes__modemsetup_gsm11 [8] = "+CLIP=1";

static const unsigned char hayes__modemsetup_gsm_id1[5] = "+GMI";
static const unsigned char hayes__modemsetup_gsm_id2[5] = "+GMM";
static const unsigned char hayes__modemsetup_gsm_id3[5] = "+GMR";
static const unsigned char hayes__modemsetup_gsm_id4[5] = "+GSN";
static const unsigned char hayes__modemsetup_gsm_id5[6] = "+CGMI";
static const unsigned char hayes__modemsetup_gsm_id6[6] = "+CGMM";
static const unsigned char hayes__modemsetup_gsm_id7[6] = "+CGMR";
static const unsigned char hayes__modemsetup_gsm_id8[6] = "+CGSN";

static const unsigned char hayes__modemsetup_gsmnok1[11] = "X5&S0S30=5";
static const unsigned char hayes__modemsetup_gsmnok2 [6] = "+DR=1";
static const unsigned char hayes__modemsetup_gsmnok3[10] = "+CSTA=129";
static const unsigned char hayes__modemsetup_gsmnok4 [8] = "+DS=0,0";
static const unsigned char hayes__modemsetup_gsmnok5 [8] = "+CLIR=1";
static const unsigned char hayes__modemsetup_gsmnok6 [8] = "+CMMS=0";

static const unsigned char hayes__modemsetup_gsmsie1[34] = \
  "\\V1^SCKS=1+CREG=1+COPS=3,2^SMGO=1";

/*--*/

static const unsigned char *hayes__modemsetup_init[K_HAYES_STDINIT_STRINGS] =
{
  hayes__modemsetup_init2, hayes__modemsetup_init3
};

static const unsigned char *hayes__modemsetup_pots_id[K_HAYES_POTSID_STRINGS] =
{
  hayes__modemsetup_pots_id1, hayes__modemsetup_pots_id2, \
  hayes__modemsetup_pots_id3
};

static const unsigned char *hayes__modemsetup_potsgal[K_HAYES_POTSGAL_STRINGS] =
{
  hayes__modemsetup_potsgal1, hayes__modemsetup_potsgal2, \
  hayes__modemsetup_potsgal3
};

static const unsigned char *hayes__modemsetup_potscir[K_HAYES_POTSCIR_STRINGS] =
{
  hayes__modemsetup_potscir1, hayes__modemsetup_potscir2, \
  hayes__modemsetup_potscir3, hayes__modemsetup_potscir4
};

static const unsigned char *hayes__modemsetup_potsels[K_HAYES_POTSELS_STRINGS] =
{
  hayes__modemsetup_potsels1, hayes__modemsetup_potsels2, \
  hayes__modemsetup_potsels3, hayes__modemsetup_potsels4, \
  hayes__modemsetup_potsels5
};

static const unsigned char *hayes__modemsetup_potsusr[K_HAYES_POTSUSR_STRINGS] =
{
  hayes__modemsetup_potsusr1, hayes__modemsetup_potsusr2, \
  hayes__modemsetup_potsusr3, hayes__modemsetup_potsusr4, \
  hayes__modemsetup_potsusr5
};


static const unsigned char *hayes__modemsetup_gsm[K_HAYES_GSMINIT_STRINGS] =
{
  hayes__modemsetup_gsm1,  hayes__modemsetup_gsm2,  hayes__modemsetup_gsm3,  \
  hayes__modemsetup_gsm4,  hayes__modemsetup_gsm5,  hayes__modemsetup_gsm6,  \
  hayes__modemsetup_gsm7,  hayes__modemsetup_gsm8,  hayes__modemsetup_gsm9,  \
  hayes__modemsetup_gsm10, hayes__modemsetup_gsm11, hayes__command_getcerr
};

static const unsigned char *hayes__modemsetup_gsm_id[K_HAYES_GSMID_STRINGS] =
{
  hayes__modemsetup_gsm_id1, hayes__modemsetup_gsm_id2, \
  hayes__modemsetup_gsm_id3, hayes__modemsetup_gsm_id4, \
  hayes__modemsetup_gsm_id5, hayes__modemsetup_gsm_id6, \
  hayes__modemsetup_gsm_id7, hayes__modemsetup_gsm_id8
};

static const unsigned char hayes__modemsetup_infolen[K_HAYES_GSMID_STRINGS] =
{
  K_HAYES_INFOLEN_MAN-1,  K_HAYES_INFOLEN_DEV-1,  K_HAYES_INFOLEN_REV-1,  \
  K_HAYES_INFOLEN_SER-1,  K_HAYES_INFOLEN_PMAN-1, K_HAYES_INFOLEN_PDEV-1, \
  K_HAYES_INFOLEN_PREV-1, K_HAYES_INFOLEN_PSER-1
};

static const unsigned char *hayes__modemsetup_gsmnok[K_HAYES_GSMNOK_STRINGS] =
{
  hayes__modemsetup_gsmnok1, hayes__modemsetup_gsmnok2, \
  hayes__modemsetup_gsmnok3, hayes__modemsetup_gsmnok4, \
  hayes__modemsetup_gsmnok5, hayes__modemsetup_gsmnok6
};

/*----*/

static const unsigned char hayes__modemcfg_potsrtscts10[4] = "\\Q0";
static const unsigned char hayes__modemcfg_potsrtscts11[4] = "\\Q3";
static const unsigned char hayes__modemcfg_potsrtscts12[4] = "\\Q1";

static const unsigned char hayes__modemcfg_potsrtscts20[7] = "\\Q0&K0";
static const unsigned char hayes__modemcfg_potsrtscts21[7] = "\\Q3&K3";
static const unsigned char hayes__modemcfg_potsrtscts22[7] = "\\Q1&K4";

static const unsigned char hayes__modemcfg_potsrtscts30[10] = "&H0&I0&R1";
static const unsigned char hayes__modemcfg_potsrtscts31[10] = "&H1&I0&R2";
static const unsigned char hayes__modemcfg_potsrtscts32[10] = "&H2&I2&R1";


static const unsigned char hayes__modemcfg_potserrco10[7] = "%C0\\N0";
static const unsigned char hayes__modemcfg_potserrco11[7] = "%C0\\N4";
static const unsigned char hayes__modemcfg_potserrco12[7] = "%C1\\N4";

static const unsigned char hayes__modemcfg_potserrco20[13] = \
  "%C0\\C0\\N0\"H0";
static const unsigned char hayes__modemcfg_potserrco21[13] = \
  "%C0\\C2\\N3\"H0";
static const unsigned char hayes__modemcfg_potserrco22[13] = \
  "%C1\\C2\\N3\"H3";

static const unsigned char hayes__modemcfg_potserrco30[10] = "%C0\\C0\\N0";
static const unsigned char hayes__modemcfg_potserrco31[10] = "%C0\\C2\\N3";
static const unsigned char hayes__modemcfg_potserrco32[10] = "%C3\\C2\\N3";

static const unsigned char hayes__modemcfg_potserrco40[7] = "&K0&M0";
static const unsigned char hayes__modemcfg_potserrco41[7] = "&K0&M4";
static const unsigned char hayes__modemcfg_potserrco42[7] = "&K2&M4";

/*--*/

static const unsigned char                                  \
  *hayes__modemcfg_potsrtscts[K_HAYES_POTSRTSCTS_WAYS][3] = \
{
  hayes__modemcfg_potsrtscts10, hayes__modemcfg_potsrtscts11, \
  hayes__modemcfg_potsrtscts12,                               \
  hayes__modemcfg_potsrtscts20, hayes__modemcfg_potsrtscts21, \
  hayes__modemcfg_potsrtscts22,                               \
  hayes__modemcfg_potsrtscts30, hayes__modemcfg_potsrtscts31, \
  hayes__modemcfg_potsrtscts32
};

static const unsigned char                                \
  *hayes__modemcfg_potserrco[K_HAYES_POTSERRCO_WAYS][3] = \
{
  hayes__modemcfg_potserrco10, hayes__modemcfg_potserrco11, \
  hayes__modemcfg_potserrco12,                              \
  hayes__modemcfg_potserrco20, hayes__modemcfg_potserrco21, \
  hayes__modemcfg_potserrco22,                              \
  hayes__modemcfg_potserrco30, hayes__modemcfg_potserrco31, \
  hayes__modemcfg_potserrco32,                              \
  hayes__modemcfg_potserrco40, hayes__modemcfg_potserrco41, \
  hayes__modemcfg_potserrco42
};

/*----*/

static const unsigned char hayes__modempoll_gsm1[5] = "+CBC";
static const unsigned char hayes__modempoll_gsm2[5] = "+CSQ";

static const unsigned char hayes__modempoll_gsmsie1[17] = "^SCKS?+CIMI^SCID";
static const unsigned char hayes__modempoll_gsmsie2[13] = "+CREG?+COPS?";
static const unsigned char hayes__modempoll_gsmsie3 [8] = "+COPS=?";
static const unsigned char hayes__modempoll_gsmsie4 [7] = "^SMGO?";

/*----*/

static const struct hayes__modeminfo_basedev                       \
                    hayes__modeminfo_base[K_HAYES_MODEMTYPE_GSM] = \
{
  { NULL,                      0,                       0, 0, 0, 0, 0, 10, 20 },
  { hayes__modemsetup_potsgal, K_HAYES_POTSGAL_STRINGS, 1, 1, 1, 0, 1,  0,  0 },
  { hayes__modemsetup_potscir, K_HAYES_POTSCIR_STRINGS, 2, 2, 1, 0, 2,  0,  0 },
  { hayes__modemsetup_potsels, K_HAYES_POTSELS_STRINGS, 1, 3, 1, 0, 3,  0,  0 },
  { hayes__modemsetup_potsusr, K_HAYES_POTSUSR_STRINGS, 3, 4, 0, 1, 4,  0,  0 }
};

/*====*/

static const unsigned char hayes__reply_ok       [3] = "OK";
static const unsigned char hayes__reply_error    [6] = "ERROR";
static const unsigned char hayes__reply_cmeerr  [13] = "+CME ERROR: ";
static const unsigned char hayes__reply_cmserr  [13] = "+CMS ERROR: ";
static const unsigned char hayes__reply_connect  [8] = "CONNECT";
static const unsigned char hayes__reply_carrier [11] = "NO CARRIER";
static const unsigned char hayes__reply_dialtone[12] = "NO DIALTONE";
static const unsigned char hayes__reply_lineerr [11] = "LINE ERROR";
static const unsigned char hayes__reply_busy     [5] = "BUSY";
static const unsigned char hayes__reply_answer  [10] = "NO ANSWER";
static const unsigned char hayes__reply_voice    [6] = "VOICE";
static const unsigned char hayes__reply_blackout[12] = "BLACKLISTED";
static const unsigned char hayes__reply_delayed1 [8] = "DELAYED";
static const unsigned char hayes__reply_delayed2[18] = "DIALING INHIBITED";
static const unsigned char hayes__reply_delayed3[12] = "DIAL LOCKED";

static const unsigned char *hayes__reply[K_HAYES_REPLY_STRINGS] =
{
  hayes__reply_ok,       hayes__reply_error,    hayes__reply_cmeerr,   \
  hayes__reply_cmserr,   hayes__reply_connect,  hayes__reply_carrier,  \
  hayes__reply_dialtone, hayes__reply_lineerr,  hayes__reply_busy,     \
  hayes__reply_answer,   hayes__reply_voice,    hayes__reply_blackout, \
  hayes__reply_delayed1, hayes__reply_delayed2, hayes__reply_delayed3
};

/*--*/

static const unsigned char hayes__event_ceer   [8] = "+CEER: ";
static const unsigned char hayes__event_riback1[9] = "RINGBACK";
static const unsigned char hayes__event_riback2[8] = "RINGING";
static const unsigned char hayes__event_ring   [5] = "RING";
static const unsigned char hayes__event_cring  [9] = "+CRING: ";
static const unsigned char hayes__event_ciddate[8] = "DATE = ";
static const unsigned char hayes__event_cidtime[8] = "TIME = ";
static const unsigned char hayes__event_cidnmbr[8] = "NMBR = ";
static const unsigned char hayes__event_cidname[8] = "NAME = ";
static const unsigned char hayes__event_cidmesg[8] = "MESG = ";
static const unsigned char hayes__event_clip   [8] = "+CLIP: ";
static const unsigned char hayes__event_carrier[8] = "CARRIER";
static const unsigned char hayes__event_cr     [6] = "+CR: ";
static const unsigned char hayes__event_dr     [6] = "+DR: ";
static const unsigned char hayes__event_cbc    [7] = "+CBC: ";
static const unsigned char hayes__event_scks   [8] = "^SCKS: ";
static const unsigned char hayes__event_scid   [8] = "^SCID: ";
static const unsigned char hayes__event_creg   [8] = "+CREG: ";
static const unsigned char hayes__event_cops   [8] = "+COPS: ";
static const unsigned char hayes__event_csq    [7] = "+CSQ: ";
static const unsigned char hayes__event_cbm    [7] = "+CBM: ";
static const unsigned char hayes__event_cmt    [7] = "+CMT: ";
static const unsigned char hayes__event_cmti   [8] = "+CMTI: ";
static const unsigned char hayes__event_cds    [7] = "+CDS: ";
static const unsigned char hayes__event_smgo   [8] = "^SMGO: ";
static const unsigned char hayes__event_csca   [8] = "+CSCA: ";
static const unsigned char hayes__event_cpms   [8] = "+CPMS: ";
static const unsigned char hayes__event_cmgl   [8] = "+CMGL: ";
static const unsigned char hayes__event_cmgr   [8] = "+CMGR: ";
static const unsigned char hayes__event_cmgw   [8] = "+CMGW: ";
static const unsigned char hayes__event_cmgc   [8] = "+CMGC: ";
static const unsigned char hayes__event_cmgs   [8] = "+CMGS: ";
static const unsigned char hayes__event_cmss   [8] = "+CMSS: ";
static const unsigned char hayes__event_cpbs   [8] = "+CPBS: ";
static const unsigned char hayes__event_cpbr   [8] = "+CPBR: ";

static const unsigned char *hayes__event[K_HAYES_EVENT_STRINGS] =
{
  hayes__event_ceer,    hayes__event_riback1, hayes__event_riback2, \
  hayes__event_ring,    hayes__event_cring,   hayes__event_ciddate, \
  hayes__event_cidtime, hayes__event_cidnmbr, hayes__event_cidname, \
  hayes__event_cidmesg, hayes__event_clip,    hayes__event_carrier, \
  hayes__event_cr,      hayes__event_dr,      hayes__event_cbc,     \
  hayes__event_scks,    hayes__event_scid,    hayes__event_creg,    \
  hayes__event_cops,    hayes__event_csq,     hayes__event_cbm,     \
  hayes__event_cmt,     hayes__event_cmti,    hayes__event_cds,     \
  hayes__event_smgo,    hayes__event_csca,    hayes__event_cpms,    \
  hayes__event_cmgl,    hayes__event_cmgr,    hayes__event_cmgw,    \
  hayes__event_cmgc,    hayes__event_cmgs,    hayes__event_cmss,    \
  hayes__event_cpbs,    hayes__event_cpbr
};

/*--*/

static const unsigned char hayes__conmsg10 [9] = "REL/MNP5";
static const unsigned char hayes__conmsg11[11] = "REL/V42BIS";
static const unsigned char hayes__conmsg12 [9] = "REL/MNP4";
static const unsigned char hayes__conmsg13 [8] = "REL/V42";

static const unsigned char hayes__conmsg20[14] = "NONE/NONE/TX=";
static const unsigned char hayes__conmsg21[13] = "MNP/NONE/TX=";
static const unsigned char hayes__conmsg22[14] = "LAPM/NONE/TX=";
static const unsigned char hayes__conmsg23[13] = "MNP/MNP5/TX=";
static const unsigned char hayes__conmsg24[14] = "LAPM/V42B/TX=";

static const unsigned char hayes__conmsg30 [5] = "MNP5";
static const unsigned char hayes__conmsg31[12] = "LAPM/V42BIS";
static const unsigned char hayes__conmsg32 [4] = "MNP";
static const unsigned char hayes__conmsg33 [5] = "LAPM";

static const unsigned char hayes__conmsg40 [5] = "NONE";
static const unsigned char hayes__conmsg41 [9] = "MNP/MNP5";
static const unsigned char hayes__conmsg4x1[4] = "ARQ/";

static const unsigned char *hayes__conmsg1[K_HAYES_CONMSG1_STRINGS] =
{
  hayes__conmsg10, hayes__conmsg11, hayes__conmsg12, hayes__conmsg13
};

static const unsigned char *hayes__conmsg2[K_HAYES_CONMSG2_STRINGS] =
{
  hayes__conmsg20, hayes__conmsg21, hayes__conmsg22, hayes__conmsg23, \
  hayes__conmsg24
};

static const unsigned char *hayes__conmsg3[K_HAYES_CONMSG3_STRINGS] =
{
  hayes__conmsg30, hayes__conmsg31, hayes__conmsg32, hayes__conmsg33
};

static const unsigned char *hayes__conmsg4[K_HAYES_CONMSG4_STRINGS] =
{
  hayes__conmsg40, hayes__conmsg41, hayes__conmsg31, hayes__conmsg32, \
  hayes__conmsg33
};

static const unsigned char *hayes__conmsg4x[1] =
{
  hayes__conmsg4x1
};

/*--*/

static const unsigned char hayes__report_cr1  [6] = "ASYNC";
static const unsigned char hayes__report_cr2  [5] = "SYNC";
static const unsigned char hayes__report_cr3 [10] = "REL ASYNC";
static const unsigned char hayes__report_cr4  [9] = "REL SYNC";
static const unsigned char hayes__report_cr5  [4] = "FAX";
static const unsigned char hayes__report_cr6 [12] = "VOICE/ASYNC";
static const unsigned char hayes__report_cr7 [11] = "VOICE/SYNC";
static const unsigned char hayes__report_cr8 [16] = "VOICE/REL ASYNC";
static const unsigned char hayes__report_cr9 [15] = "VOICE/REL SYNC";
static const unsigned char hayes__report_cr10 [6] = "VOICE";
static const unsigned char hayes__report_cr11[16] = "ALT VOICE/ASYNC";
static const unsigned char hayes__report_cr12[15] = "ALT VOICE/SYNC";
static const unsigned char hayes__report_cr13[20] = "ALT VOICE/REL ASYNC";
static const unsigned char hayes__report_cr14[19] = "ALT VOICE/REL SYNC";
static const unsigned char hayes__report_cr15[14] = "ALT VOICE/FAX";
static const unsigned char hayes__report_cr16[16] = "ALT ASYNC/VOICE";
static const unsigned char hayes__report_cr17[15] = "ALT SYNC/VOICE";
static const unsigned char hayes__report_cr18[20] = "ALT REL ASYNC/VOICE";
static const unsigned char hayes__report_cr19[19] = "ALT REL SYNC/VOICE";
static const unsigned char hayes__report_cr20[14] = "ALT FAX/VOICE";
static const unsigned char hayes__report_cr21 [5] = "GPRS";

static const unsigned char hayes__report_dr1  [5] = "NONE";
static const unsigned char hayes__report_dr2  [5] = "V42B";

static const unsigned char *hayes__report_cr[K_HAYES_REPORTCR_STRINGS] =
{
  hayes__report_cr1,  hayes__report_cr2,  hayes__report_cr3,  \
  hayes__report_cr4,  hayes__report_cr5,  hayes__report_cr6,  \
  hayes__report_cr7,  hayes__report_cr8,  hayes__report_cr9,  \
  hayes__report_cr10, hayes__report_cr11, hayes__report_cr12, \
  hayes__report_cr13, hayes__report_cr14, hayes__report_cr15, \
  hayes__report_cr16, hayes__report_cr17, hayes__report_cr18, \
  hayes__report_cr19, hayes__report_cr20, hayes__report_cr21
};

static const unsigned char *hayes__report_dr[K_HAYES_REPORTDR_STRINGS] =
{
  hayes__report_dr1, hayes__report_dr2
};


unsigned char hayescommand[128];
unsigned char hayesevent  [128];
/*############################################################################*/

/*--------------*/
/*  OPEN/CLOSE  */
/*--------------*/
                                                         /* Open Hayes-Driver */
signed short hayes_open (struct hayes_data *dseg, \
                         const struct hayes_config *cfg)
{
  struct sio_config siocfg;
  signed short n;

  /* Check pointer */
  if ((dseg == NULL) || (cfg == NULL))
    return E_HAYES_NULLPTR;

  /* Check parameter */
  if (cfg->modem_type >= K_HAYES_MODEMTYPES)
    return E_HAYES_CFG_MDMTYPE;

  /* Setup variables */
  memcpy (&(dseg->cfg), cfg, sizeof (struct hayes_config));
  dseg->cfg.modem_initstr[K_HAYES_INITSTRBUFLEN-1] = 0;
  dseg->cfg.modem_dialpfx[K_HAYES_DIALPFXBUFLEN-1] = 0;
  dseg->callstate = K_HAYES_CALLSTATE_IDLE;

  /* Open serial port */
  siocfg.sigterm  = cfg->sigterm;
  siocfg.portnum  = cfg->sio_portnum;
  siocfg.porthnd  = INVALID_HANDLE_VALUE;
  siocfg.obxport  = cfg->sio_obxport;
  siocfg.rilport  = cfg->sio_rilport;
  siocfg.baudrate = cfg->sio_baudrate;
  siocfg.flowctrl = cfg->sio_flowctrl;
  siocfg.usedtr   = cfg->sio_usedtr;
  siocfg.usedsr   = cfg->sio_usedsr;
  siocfg.usedcd   = cfg->sio_usedcd;
  siocfg.usering  = cfg->sio_usering;
  n = sio_open (&(dseg->sio), &siocfg);
  if (n < 0)
    return n;

  /* Init modem */
  n = hayes_setup (dseg);
  if (n < 0)
    sio_close (&(dseg->sio));
  return n;
}

/*----*/

signed short hayes_close (struct hayes_data *dseg)      /* Close Hayes-Driver */
{
  signed short n,m;

  /* Ptr-Check, Hangup */
  n = hayes__hangup (dseg, 1);
  if (n == E_HAYES_NULLPTR)
    return n;

  /* Close serial port */
  m = sio_close (&(dseg->sio));
  if ((m < 0) && (n >= 0))
    n = m;
  return n;
}

/*---------*/
/*  SETUP  */
/*---------*/

signed short hayes_setup (struct hayes_data *dseg)             /* Setup Modem */
{
  unsigned char *infolen[K_HAYES_GSMID_STRINGS];
  unsigned char *infobuf[K_HAYES_GSMID_STRINGS];
  const struct hayes__modeminfo_basedev *mbinfo;
  unsigned char x,y;
  signed short n;

  /* Check pointer */
  if (dseg == NULL)
    return E_HAYES_NULLPTR;

  /* Setup variables */
  dseg->ptmr_pwr         = 0;
  dseg->ptmr_sim         = 0;
  dseg->ptmr_net         = 0;
  dseg->ptmr_sig         = 0;
  dseg->ptmr_nlist       = 0;
  dseg->ptmr_sms         = 0;
  dseg->error_cme        = 0xFFFF;
  dseg->error_cms        = 0xFFFF;
  dseg->error_txt        = 0;
  dseg->error_txtbuf[0]  = 0;
  dseg->ring_event       = 0;
  dseg->ring_clip        = 0;
  dseg->ring_clipfmt     = 0xFF;
  dseg->ring_clipbuf[0]  = 0;
  dseg->link_serv        = 0xFF;
  dseg->link_prot        = 0xFF;
  dseg->link_rate        = 0xFFFFFFFFL;
  dseg->gsm_mainpwr      = 0xFF;
  dseg->gsm_battlvl      = 0xFF;
  dseg->gsm_simstat      = 0xFF;
  dseg->gsm_netlogin     = 0xFF;
  dseg->gsm_netnum       = 0xFFFF;
  dseg->gsm_netlac       = 0xFFFF;
  dseg->gsm_netcid       = 0xFFFF;
  dseg->gsm_rxsiglvl     = 0xFF;
  dseg->gsm_rxsigqual    = 0xFF;
  dseg->info_man         = 0;
  dseg->info_dev         = 0;
  dseg->info_rev         = 0;
  dseg->info_ser         = 0;
  dseg->info_pman        = 0;
  dseg->info_pdev        = 0;
  dseg->info_prev        = 0;
  dseg->info_pser        = 0;
  dseg->info_imsi        = 0;
  dseg->info_simnr       = 0;
  dseg->infobuf_man  [0] = 0;
  dseg->infobuf_dev  [0] = 0;
  dseg->infobuf_rev  [0] = 0;
  dseg->infobuf_ser  [0] = 0;
  dseg->infobuf_pman [0] = 0;
  dseg->infobuf_pdev [0] = 0;
  dseg->infobuf_prev [0] = 0;
  dseg->infobuf_pser [0] = 0;
  dseg->infobuf_imsi [0] = 0;
  dseg->infobuf_simnr[0] = 0;
  for (x=0; x<K_HAYES_NETLIST_LINES; x++)
  {
    dseg->netlist_state  [x]    = 0xFF;
    dseg->netlist_netnum [x]    = 0xFFFF;
    dseg->netlist_namelen[x]    = 0;
    dseg->netlist_namebuf[x][0] = 0;
  }
  dseg->sms_overflow = 0xFF;

  /* Build info-list */
  infolen[0] = &(dseg->info_man);
  infolen[1] = &(dseg->info_dev);
  infolen[2] = &(dseg->info_rev);
  infolen[3] = &(dseg->info_ser);
  infolen[4] = &(dseg->info_pman);
  infolen[5] = &(dseg->info_pdev);
  infolen[6] = &(dseg->info_prev);
  infolen[7] = &(dseg->info_pser);
  infobuf[0] = dseg->infobuf_man;
  infobuf[1] = dseg->infobuf_dev;
  infobuf[2] = dseg->infobuf_rev;
  infobuf[3] = dseg->infobuf_ser;
  infobuf[4] = dseg->infobuf_pman;
  infobuf[5] = dseg->infobuf_pdev;
  infobuf[6] = dseg->infobuf_prev;
  infobuf[7] = dseg->infobuf_pser;

  /* Hangup */
  n = hayes__hangup (dseg, 0);
  if (n < 0)
    return n;

  /* Reset modem */
  x = K_HAYES_TIMEOUT_RESET;
  y = 0;
  n = hayes__command (dseg, 1, 0, 0, &x, &y, 0, 0, hayes__command_reset);
  if (n < 0)
    return n;

  /* Common setup */
  x = K_HAYES_TIMEOUT_COMMAND;
  y = 0;
  n = hayes__command (dseg, 1, 0, 0, &x, &y, 0, 1, hayes__modemsetup_init1);
  if (n)
    goto done;
  n = hayes__runbatch (dseg, hayes__modemsetup_init, K_HAYES_STDINIT_STRINGS);
  if (n < 0)
    return n;

  /* Pots setup */
  if (dseg->cfg.modem_type < K_HAYES_MODEMTYPE_GSM)
  {
    mbinfo = &(hayes__modeminfo_base[dseg->cfg.modem_type]);
    x = K_HAYES_TIMEOUT_COMMAND;
    y = 0;
    n = hayes__command (dseg, 1, 0, 0, &x, &y, 1, 1, hayes__modemsetup_pots1);
    if (n)
      goto done;
    n = hayes__askvalues (dseg, infolen, infobuf, hayes__modemsetup_infolen, \
                          hayes__modemsetup_pots_id, K_HAYES_POTSID_STRINGS);
    if (n < 0)
      return n;
    if (mbinfo->initstr != NULL)
    {
      n = hayes__runbatch (dseg, mbinfo->initstr, mbinfo->initcnt);
      if (n < 0)
        return n;
    }
    dseg->textposx = 0;
    x = 0x30 + (dseg->cfg.modem_pots_volume >> 6);
    hayes__writechar (dseg, 0x4C); /* L {0..3} */
    hayes__writechar (dseg, x);
    x = 0x30 + (dseg->cfg.modem_pots_volume ? 1 : 0);
    hayes__writechar (dseg, 0x4D); /* M {0..1} */
    hayes__writechar (dseg, x);
    x = 0x31 + ((dseg->cfg.modem_pots_dialtone ? 1 : 0) \
              | (dseg->cfg.modem_pots_busytone ? 2 : 0));
    if ((x >= 0x33) && (mbinfo->atxmd))
      x += 2;
    hayes__writechar (dseg, 0x58); /* X {1..6} */
    hayes__writechar (dseg, x);
    if (mbinfo->rtscts)            /* Handshake */
      hayes__writestr (dseg, hayes__modemcfg_potsrtscts[(mbinfo->rtscts)-1] \
                       [dseg->cfg.sio_flowctrl]);
    hayes__writechar (dseg, 0);
    x = K_HAYES_TIMEOUT_COMMAND;
    y = 0;
    n = hayes__command (dseg, 1, 0, 0, &x, &y, 1, 1, dseg->textbuf[0]);
    if (n)
      goto done;
  }

  /* Gsm setup */
  else
  {
    n = hayes__runbatch (dseg, hayes__modemsetup_gsm, K_HAYES_GSMINIT_STRINGS);
    if (n < 0)
      return n;
    n = hayes__askvalues (dseg, infolen, infobuf, hayes__modemsetup_infolen, \
                          hayes__modemsetup_gsm_id, K_HAYES_GSMID_STRINGS);
    if (n < 0)
      return n;
        if (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_GSM_NOK)
    {
      n = hayes__runbatch (dseg, hayes__modemsetup_gsmnok, \
                           K_HAYES_GSMNOK_STRINGS);
      if (n < 0)
        return n;
    }
    else if (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_GSM_SIE)
    {
      dseg->textposx = 0;
      x = 0x30 + (((dseg->cfg.sio_flowctrl == K_SIO_FLOWCTRL_RTSCTS)  ? 3 : 0) \
                | ((dseg->cfg.sio_flowctrl == K_SIO_FLOWCTRL_XONXOFF) ? 1 : 0));
      hayes__writestr (dseg, hayes__cmdstr_bq); /* \Q {0..3} */
      hayes__writechar (dseg, x);
      hayes__writestr (dseg, hayes__modemsetup_gsmsie1);
      hayes__writechar (dseg, 0);
      x = K_HAYES_TIMEOUT_COMMAND;
      y = 0;
      n = hayes__command (dseg, 1, 0, 0, &x, &y, 1, 1, dseg->textbuf[0]);
      if (n)
        goto done;
    }
  }

  /* User setup */
  if (dseg->cfg.modem_initstr[0])
  {
    x = K_HAYES_TIMEOUT_COMMAND;
    y = 0;
    n = hayes__command (dseg, 1, 0, 0, &x, &y, 1, 1, dseg->cfg.modem_initstr);
    if (n)
    {
      done:
      if (n > 0)
        n = E_HAYES_MDM_OK - n;
      return n;
    }
  }

  /* Poll status */
  n = hayes__pollgsm (dseg, 0, 0);
  return n;
}

/*-----------*/
/*  MONITOR  */
/*-----------*/
                                                               /* Watch Modem */
signed short hayes_watch (struct hayes_data *dseg, struct hayes_link *link, \
                          unsigned char clrclip)
{
  unsigned char x;
  signed short n;

  /* Ptr-Check, Hangup */
  if (link == NULL)
    return E_HAYES_NULLPTR;
  n = hayes__hangup (dseg, 0);
  if (n < 0)
    return n;

  /* Watch loop */
  if (clrclip)
  {
    dseg->ring_clip = 0;
    dseg->ring_clipfmt = 0xFF;
    dseg->ring_clipbuf[0] = 0;
    dseg->link_serv = 0xFF;
    dseg->link_prot = 0xFF;
  }
  while (42)
  {
    dseg->textlines = 0;
    x = 1;
    n = hayes__getline (dseg, 1, 0, 1, &x);
    if ((n == E_HAYES_GET_NOREPLY) || (n == E_HAYES_GET_NOCHAR))
    {
      n = hayes__pollgsm (dseg, 0, 1);
      if (n == E_SIO_LINE_RINGING)
        goto hring;
      else if (n == E_HAYES_EVT_RINGING)
        goto ringing;
      break;
    }
    else if (n == E_SIO_LINE_RINGING)
    {
      hring:
      n = sio_txdelay (&(dseg->sio), 1, 0, 0, K_HAYES_DELAY_HWRING);
      if (n < 0)
        break;
      goto ringing;
    }
    else if ((n == K_HAYES_EVENT_RING) || (n == K_HAYES_EVENT_CRING))
    {
      ringing:
      link->dialout = 0;
      x = dseg->ring_clip;
      if (x > K_HAYES_DIALNUMBUFLEN-1)
        x = K_HAYES_DIALNUMBUFLEN-1;
      link->remnumlen = x;
      if (x)
        memcpy (link->remnumbuf, dseg->ring_clipbuf, x);
      link->remnumbuf[x] = 0;
      link->service  = K_HAYES_LINKSERVICE_UNKNOWN;
      link->carrier  = K_HAYES_LINKCARRIER_UNKNOWN;
      link->protocol = K_HAYES_LINKPROTOCOL_UNKNOWN;
      link->baudrate = 0L;
      if (dseg->link_serv < 0xFE)
        link->service = dseg->link_serv;
      if (dseg->link_prot < 0xFE)
        link->protocol = dseg->link_prot;
      n = 1;
      break;
    }
    else if ((n < 0) && (n != E_HAYES_GET_ERRDATA))
      break;
  }
  return n;
}

/*-----------*/
/*  CONNECT  */
/*-----------*/
                                                              /* Connect Call */
signed short hayes_connect (struct hayes_data *dseg, struct hayes_link *link)
{
  const struct hayes__modeminfo_basedev *mbinfo;
  unsigned char x,y;
  unsigned short w;
  signed short n,m;

  /* Ptr-Check, Hangup */
  if (link == NULL)
    return E_HAYES_NULLPTR;
  n = hayes__hangup (dseg, 0);
  if (n < 0)
    return n;

  /* Check parameter */
  if (link->remnumlen >= K_HAYES_DIALNUMBUFLEN-1)
    link->remnumlen = K_HAYES_DIALNUMBUFLEN-1;
  link->remnumbuf[link->remnumlen] = 0;
  if (link->service == K_HAYES_LINKSERVICE_UNKNOWN)
    link->service = K_HAYES_LINKSERVICE_DATA;
  if (link->carrier == K_HAYES_LINKCARRIER_UNKNOWN)
  {
    if (dseg->cfg.modem_type < K_HAYES_MODEMTYPE_GSM)
      link->carrier = K_HAYES_LINKCARRIER_ANA_V32;
    else
      link->carrier = K_HAYES_LINKCARRIER_DIG_V110;
  }
  if (link->protocol == K_HAYES_LINKPROTOCOL_UNKNOWN)
    link->protocol = K_HAYES_LINKPROTOCOL_RAW;
  if (link->service != K_HAYES_LINKSERVICE_DATA)
    return E_HAYES_PAR_LINKSERV;
  if (link->carrier >= K_HAYES_LINKCARRIERS)
    return E_HAYES_PAR_LINKCARR;
  if (link->protocol >= K_HAYES_LINKPROTOCOLS)
    return E_HAYES_PAR_LINKPROT;

  /* Set parameter */
  dseg->textposx  = 0;
  if (dseg->cfg.modem_type < K_HAYES_MODEMTYPE_GSM)
  {
    mbinfo = &(hayes__modeminfo_base[dseg->cfg.modem_type]);
    if (mbinfo->errco)                           /* Errco */
      hayes__writestr (dseg, hayes__modemcfg_potserrco[(mbinfo->errco)-1] \
                       [link->protocol]);
  }
  else
  {
    hayes__writestr (dseg, hayes__cmdstr_cbst1); /* +CBST={7/71},0,1 */
    if (link->carrier == K_HAYES_LINKCARRIER_DIG_V110)
      hayes__writechar (dseg, 0x31);
    hayes__writestr (dseg, hayes__cmdstr_cbst2);
    hayes__writechar (dseg, 0);
    x = K_HAYES_TIMEOUT_COMMAND;
    y = 0;
    n = hayes__command (dseg, 1, 0, link->dialout, &x, &y, 1, 1, \
                        dseg->textbuf[0]);
    if (!n && (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_GSM_NOK))
    {
      dseg->textposx = 0;
      x = 0x30 + ((link->protocol >= K_HAYES_LINKPROTOCOL_RELCOMP) ? 3 : 0);
      hayes__writestr (dseg, hayes__cmdstr_ds1); /* +DS={0..3},0 */
      hayes__writechar (dseg, x);
      hayes__writestr (dseg, hayes__cmdstr_ds2);
      hayes__writechar (dseg, 0);
      x = K_HAYES_TIMEOUT_COMMAND;
      y = 0;
      n = hayes__command (dseg, 1, 0, link->dialout, &x, &y, 1, 1, \
                          dseg->textbuf[0]);
    }
    if (n)
      goto err_exit2;
    dseg->textposx  = 0;
  }

  /* Connect modems */
  dseg->link_serv = 0xFF;
  dseg->link_prot = 0xFF;
  dseg->link_rate = 0xFFFFFFFFL;
  if (!(link->dialout))
  {
    hayes__writechar (dseg, 0x41); /* A */
    x = K_HAYES_TIMEOUT_DIALIN;
  }
  else
  {
    hayes__writechar (dseg, 0x44); /* D */
    if (dseg->cfg.modem_type < K_HAYES_MODEMTYPE_GSM)
    {
      if (dseg->cfg.modem_pots_tonedial)
        hayes__writechar (dseg, 0x54); /* T */
      else
        hayes__writechar (dseg, 0x50); /* P */
    }
    hayes__writestr (dseg, dseg->cfg.modem_dialpfx);
    hayes__writestr (dseg, link->remnumbuf);
    x = K_HAYES_TIMEOUT_DIALOUT;
  }
  hayes__writechar (dseg, 0);
  y = 0;
  dseg->callstate = K_HAYES_CALLSTATE_CONNECTING;
  n = hayes__command (dseg, 1, 0, link->dialout, &x, &y, 1, 1, \
                      dseg->textbuf[0]);

  /* Post-connect */
  dseg->error_txt = 0;
  dseg->error_txtbuf[0] = 0;
  if ((n == E_SIO_LINE_RINGING) \
   || ((n <= E_HAYES_PUT_NOECHO) && (n >= E_HAYES_EVT_RINGING))
   || ((n >= K_HAYES_REPLY_OK)   && (n <= K_HAYES_REPLY_UNKNOWN)))
  {
    m = hayes__hangup (dseg, 1);
    if (m < 0)
      n = m;
    else if (n >= 0)
      goto get_errinfo;
  }
  else if (n == K_HAYES_REPLY_CONNECT)
  {
    n = 0;
    dseg->callstate = K_HAYES_CALLSTATE_CONNECTED;
    link->service   = K_HAYES_LINKSERVICE_UNKNOWN;
    link->carrier   = K_HAYES_LINKCARRIER_UNKNOWN;
    link->protocol  = K_HAYES_LINKPROTOCOL_UNKNOWN;
    link->baudrate  = 0L;
    if (dseg->link_serv < 0xFE)
      link->service = dseg->link_serv;
    if (dseg->link_prot < 0xFE)
      link->protocol = dseg->link_prot;
    if (dseg->link_rate < 1000000L)
    {
      link->baudrate = dseg->link_rate;
      n = (signed short)(dseg->link_rate / 100);
    }
  }
  else if (n >= 0)
  {
    dseg->callstate = K_HAYES_CALLSTATE_IDLE;
    get_errinfo:
    n = E_HAYES_MDM_OK - n;
    dseg->error_txt = 0;
    dseg->error_txtbuf[0] = 0;
    if (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_POTS_ELS)
    {
      x = K_HAYES_TIMEOUT_COMMAND;
      y = 1;
      m = hayes__command (dseg, 1, 0, link->dialout, &x, &y, 1, 1, \
                          hayes__command_geterr1);
      if (m)
        goto err_exit1;
      else if (y)
      {
        w = dseg->textlen[0];
        if (w >= K_HAYES_ERRORTXTLEN)
          w = K_HAYES_ERRORTXTLEN - 1;
        if (w)
          memcpy (dseg->error_txtbuf, dseg->textbuf[0], w);
        dseg->error_txtbuf[w] = 0;
        dseg->error_txt = (unsigned char)w;
      }
    }
    else if (dseg->cfg.modem_type >= K_HAYES_MODEMTYPE_GSM)
    {
      x = K_HAYES_TIMEOUT_COMMAND;
      y = 0;
      m = hayes__command (dseg, 1, 0, link->dialout, &x, &y, 1, 1, \
                          hayes__command_getcerr);
      if (m)
      {
        err_exit1:
        n = m;
        err_exit2:
        if (n >= 0)
          n = E_HAYES_MDM_OK - n;
      }
    }
  }
  return n;
}

/*----------*/
/*  HANGUP  */
/*----------*/

signed short hayes_hangup (struct hayes_data *dseg)            /* Hangup Call */
{
  signed short n;

  /* Ptr-Check, Hangup */
  n = hayes__hangup (dseg, 1);
  return n;
}

/*============================================================================*/
                                                            /* Terminate Call */
static signed short hayes__hangup (struct hayes_data *dseg, unsigned char check)
{
  const unsigned char *p;
  unsigned char x,y;
  signed short n;

  /* Check pointer */
  if (dseg == NULL)
    return E_HAYES_NULLPTR;

  /* Hangup modem */
  n = 0;
  if (check || (dseg->callstate > K_HAYES_CALLSTATE_IDLE))
  {
    /* Escape sequence */
    dseg->callstate = K_HAYES_CALLSTATE_IDLE;
    n = sio_txclear (&(dseg->sio), 0, 0, 0);
    if (n < 0)
      return n;
    if (dseg->cfg.sio_usedtr)
    {
      n = sio_pulse (&(dseg->sio), 0, 0, 0);
      if (n < 0)
        return n;
    }
    else
    {
      n = sio_txdelay (&(dseg->sio), 0, 0, 0, K_HAYES_DELAY_ESCAPE1);
      if (n < 0)
        return n;
      n = hayes__putch (dseg, 0, 0, 0, 0, 0x18); /* ^X */
      if (n < 0)
        return n;
      x = 0;
      if (dseg->cfg.modem_type < K_HAYES_MODEMTYPE_GSM)
        x = hayes__modeminfo_base[dseg->cfg.modem_type].escape;
      if (!x)
      {
        n = sio_txdelay (&(dseg->sio), 0, 0, 0, K_HAYES_DELAY_ESCAPE2);
        if (n < 0)
          return n;
      }
      for (y=0; y<3; y++)
      {
        n = hayes__putch (dseg, 0, 0, 0, 0, 0x2B); /* + */
        if (n < 0)
          return n;
      }
      if (!x)
      {
        n = sio_txdelay (&(dseg->sio), 0, 0, 0, K_HAYES_DELAY_ESCAPE3);
        if (n < 0)
          return n;
      }
      else
      {
        n = hayes__putch (dseg, 0, 0, 0, 0, 0x41); /* A */
        if (n < 0)
          return n;
        n = hayes__putch (dseg, 0, 0, 0, 0, 0x54); /* T */
        if (n < 0)
          return n;
        n = hayes__putch (dseg, 0, 0, 0, 0, 0x0D); /* <CR> */
        if (n < 0)
          return n;
        n = sio_txdelay (&(dseg->sio), 0, 0, 0, K_HAYES_DELAY_ESCAPE4);
        if (n < 0)
          return n;
      }
    }
    n = sio_rxerror (&(dseg->sio), 0, 0, 0, 1);
    if (n < 0)
      return n;
    n = sio_rxclear (&(dseg->sio), 0, 0, 0);
    if (n < 0)
      return n;

    /* Hangup command */
    if (dseg->cfg.modem_type < K_HAYES_MODEMTYPE_GSM)
      p = hayes__command_hangup;
    else
      p = hayes__command_hangsm;
    x = K_HAYES_TIMEOUT_HANGUP;
    y = 0;
    n = hayes__command (dseg, 0, 0, 0, &x, &y, 1, 1, p);
    if (n == K_HAYES_REPLY_CARRIER)
      n = 0;
    if (n > 0)
      n = E_HAYES_MDM_OK - n;
  }
  return n;
}

/*----*/
                                                            /* Poll Gsm-State */
static signed short hayes__pollgsm (struct hayes_data *dseg, \
                                    unsigned char dcd, unsigned char ring)
{
  unsigned char s,x,y;
  unsigned short w;
  signed short n;

  /* Poll power */
  s = 0;
  x = K_HAYES_TIMEOUT_COMMAND;
  y = 0;
  dseg->textposx = 0;
  if (dseg->ptmr_pwr)
    dseg->ptmr_pwr--;
  else
  {
    dseg->ptmr_pwr = K_HAYES_POLLRATE_PWR-1;
    if (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_GSM_SIE)
    {
      s = 1;
      hayes__writestr (dseg, hayes__modempoll_gsm1);
    }
    else if (dseg->cfg.modem_type >= K_HAYES_MODEMTYPE_GSM)
    {
      n = hayes__command (dseg, 1, dcd, ring, &x, &y, 1, 1, \
                          hayes__modempoll_gsm1);
      if (n)
        goto done;
    }
  }
  /* Poll sim */
  if (dseg->ptmr_sim)
    dseg->ptmr_sim--;
  else
  {
    dseg->ptmr_sim = K_HAYES_POLLRATE_SIM-1;
    if (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_GSM_SIE)
    {
      s = 1;
      y = 1;
      hayes__writestr (dseg, hayes__modempoll_gsmsie1);
    }
  }
  /* Poll network */
  if (dseg->ptmr_net)
    dseg->ptmr_net--;
  else
  {
    dseg->ptmr_net = K_HAYES_POLLRATE_NET-1;
    if (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_GSM_SIE)
    {
      s = 1;
      hayes__writestr (dseg, hayes__modempoll_gsmsie2);
    }
  }
  /* Poll signal */
  if (dseg->ptmr_sig)
    dseg->ptmr_sig--;
  else
  {
    dseg->ptmr_sig = K_HAYES_POLLRATE_SIG-1;
    if (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_GSM_SIE)
    {
      s = 1;
      hayes__writestr (dseg, hayes__modempoll_gsm2);
    }
    else if (dseg->cfg.modem_type >= K_HAYES_MODEMTYPE_GSM)
    {
      x = K_HAYES_TIMEOUT_COMMAND;
      y = 0;
      n = hayes__command (dseg, 1, dcd, ring, &x, &y, 1, 1, \
                          hayes__modempoll_gsm2);
      if (n)
        goto done;
    }
  }
  /* Poll netlist */
  if (dseg->ptmr_nlist)
    dseg->ptmr_nlist--;
  else
  {
    dseg->ptmr_nlist = K_HAYES_POLLRATE_NLIST-1;
    if (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_GSM_SIE)
    {
      s = 1;
      x = K_HAYES_TIMEOUT_NETLIST;
      hayes__writestr (dseg, hayes__modempoll_gsmsie3);
    }
  }
  /* Poll sms */
  if (dseg->ptmr_sms)
    dseg->ptmr_sms--;
  else
  {
    dseg->ptmr_sms = K_HAYES_POLLRATE_SMS-1;
    if (dseg->cfg.modem_type == K_HAYES_MODEMTYPE_GSM_SIE)
    {
      s = 1;
      hayes__writestr (dseg, hayes__modempoll_gsmsie4);
    }
  }
  /* Send cmdline */
  n = 0;
  if (s)
  {
    hayes__writechar (dseg, 0);
    n = hayes__command (dseg, 1, dcd, ring, &x, &y, 1, 1, dseg->textbuf[0]);
    if ((!n) && y)
    {
      w = dseg->textlen[0];
      if (w > K_HAYES_INFOLEN_IMSI-1)
        w = K_HAYES_INFOLEN_IMSI-1;
      if (w)
        memcpy (dseg->infobuf_imsi, dseg->textbuf[0], w);
      dseg->infobuf_imsi[w] = 0;
      dseg->info_imsi = (unsigned char)w;
    }
  }
  done:
  if (n > 0)
    n = E_HAYES_MDM_OK - n;
  return n;
}

/*====*/
                                                       /* Exec Value-Requests */
static signed short hayes__askvalues (struct hayes_data *dseg,       \
                                      unsigned char **lenlist,       \
                                      unsigned char **buflist,       \
                                      const unsigned char *maxlens,  \
                                      const unsigned char **cmdlist, \
                                      unsigned char listlen)
{
  unsigned char c,x,y;
  unsigned short w;
  signed short n;

  n = 0;
  for (c=0; c<listlen; c++)
  {
    x = K_HAYES_TIMEOUT_COMMAND;
    y = 1;
    n = hayes__command (dseg, 1, 0, 0, &x, &y, 1, 1, cmdlist[c]);
    if (n)
      break;
    w = 0;
    if (y)
    {
      w = dseg->textlen[0];
      if (w > maxlens[c])
        w = maxlens[c];
      if (w)
        memcpy (buflist[c], dseg->textbuf[0], w);
    }
    *(lenlist[c]) = (unsigned char)w;
    *(buflist[c] + w) = 0;
  }
  if (n > 0)
    n = E_HAYES_MDM_OK - n;
  return n;
}

/*----*/
                                                        /* Exec Command-Batch */
static signed short hayes__runbatch (struct hayes_data *dseg,       \
                                     const unsigned char **cmdlist, \
                                     unsigned char listlen)
{
  unsigned char c,x,y;
  signed short n;

  n = 0;
  for (c=0; c<listlen; c++)
  {
    x = K_HAYES_TIMEOUT_COMMAND;
    y = 0;
    n = hayes__command (dseg, 1, 0, 0, &x, &y, 1, 1, cmdlist[c]);
    if (n)
      break;
  }
  if (n > 0)
    n = E_HAYES_MDM_OK - n;
  return n;
}

/*----*/
                                                        /* Exec Hayes-Command */
static signed short hayes__command (struct hayes_data *dseg,                   \
                                    unsigned char term, unsigned char dcd,     \
                                    unsigned char ring,                        \
                                    unsigned char *timeout,                    \
                                    unsigned char *vlines, unsigned char echo, \
                                    unsigned char reply,                       \
                                    const unsigned char *data)
{
  unsigned char x,y;
  signed short n,m;
  unsigned short w;
  unsigned long d;

  /* Send command */
strcpy (hayescommand, data);
wintop_notify (K_WINTOP_PARSTATE);
  do
  {
    x = 0;
    n = hayes__getline (dseg, term, dcd, ring, &x);
  } while ((n >= 0) || (n == E_HAYES_GET_ERRDATA));
  if ((n > E_HAYES_GET_NOREPLY) || (n < E_HAYES_GET_NOCHAR))
    return n;
  n = hayes__putch (dseg, term, dcd, ring, echo, 0x41); /* A */
  if (n < 0)
    return n;
  n = hayes__putch (dseg, term, dcd, ring, echo, 0x54); /* T */
  if (n < 0)
    return n;
  while (*data)
  {
    n = hayes__putch (dseg, term, dcd, ring, echo, *data++);
    if (n < 0)
      return n;
  }
  if (!echo)
  {
    n = sio_txdelay (&(dseg->sio), term, dcd, ring, K_HAYES_DELAY_ECHO);
    if (n < 0)
      return n;
    n = sio_rxerror (&(dseg->sio), term, dcd, ring, 1);
    if (n < 0)
      return n;
    n = sio_rxclear (&(dseg->sio), term, dcd, ring);
    if (n < 0)
      return n;
  }
  n = hayes__putch (dseg, term, dcd, ring, echo, 0x0D); /* <CR> */
  if (n < 0)
    return n;

  /* Process reply */
  if (reply)
  {
    /* Get reply */
    dseg->textlines  = 0;
    dseg->ring_event = 0;
    x = 0;
    while (42)
    {
      n = hayes__getline (dseg, term, dcd, ring, timeout);
      if (n < 0)
        return n;
      if (n > 0)
        continue;
      n = hayes__readtok (dseg, hayes__reply, K_HAYES_REPLY_STRINGS);
      if (n >= 0)
      {
        if (n)
          n++;
        if (n == K_HAYES_REPLY_DELAYED)
          n = K_HAYES_REPLY_BLACKLIST;
        else if (n > K_HAYES_REPLY_DELAYED)
          n = K_HAYES_REPLY_DELAYED;
        break;
      }
      if (x >= *vlines)
      {
        n = K_HAYES_REPLY_UNKNOWN;
        break;
      }
      if (++x < K_HAYES_TEXTBUFLINES)
        dseg->textlines = x;
    }
    *vlines = dseg->textlines;

    /* Handle reply */
    if (n == K_HAYES_REPLY_CMEERR)
    {
      m = hayes__readnum (dseg, &d, 10, 6);
      if ((m == -1) || (m == 0x2C))
      {
        if (d >= 0xFF00L)
          dseg->error_cme = 0xFFFD;
        else
          dseg->error_cme = (unsigned short)d;
      }
      else
        dseg->error_cme = 0xFFFE;
    }
    else if (n == K_HAYES_REPLY_CMSERR)
    {
      m = hayes__readnum (dseg, &d, 10, 6);
      if ((m == -1) || (m == 0x2C))
      {
        if (d >= 0xFF00L)
          dseg->error_cms = 0xFFFD;
        else
          dseg->error_cms = (unsigned short)d;
      }
      else
        dseg->error_cms = 0xFFFE;
    }
    else if (n == K_HAYES_REPLY_CONNECT)
    {
      m = hayes__readchar (dseg);
      if (m == 0x20)
      {
        m = hayes__readnum (dseg, &d, 10, 7);
        if ((m != -1) && (m != 0x20) && (m != 0x2C) && (m != 0x2F))
          d = 0xFFFFFFFEL;
        else
        {
          if (d >= 1000000L)
            d = 0xFFFFFFFDL;
          x = 0;
          if (dseg->cfg.modem_type < K_HAYES_MODEMTYPE_GSM)
            x = hayes__modeminfo_base[dseg->cfg.modem_type].conmsg;
          switch (x)
          {
            case 1: /*gal*/
              if (m == -1)
                goto linkpar_raw;
              else if (m == 0x20)
              {
                m = hayes__readtok (dseg, hayes__conmsg1, \
                                    K_HAYES_CONMSG1_STRINGS);
                if ((m == 0) || (m == 1))
                  goto linkpar_relcomp;
                else if ((m == 2) || (m == 3))
                  goto linkpar_rel;
                else
                  goto linkpar_error;
              }
              break;
            case 2: /*cir*/
              if (m == 0x2F)
              {
                y = 255;
                m = hayes__readstr (dseg, NULL, &y);
                if (m != 0x2F)
                  break;
                m = hayes__readtok (dseg, hayes__conmsg2, \
                                    K_HAYES_CONMSG2_STRINGS);
                if (m == 0)
                {
                  dseg->link_serv = K_HAYES_LINKSERVICE_DATA;
                  dseg->link_prot = K_HAYES_LINKPROTOCOL_RAW;
                }
                else if ((m == 1) || (m == 2))
                {
                  dseg->link_serv = K_HAYES_LINKSERVICE_DATA;
                  dseg->link_prot = K_HAYES_LINKPROTOCOL_REL;
                }
                else if ((m == 3) || (m == 4))
                {
                  dseg->link_serv = K_HAYES_LINKSERVICE_DATA;
                  dseg->link_prot = K_HAYES_LINKPROTOCOL_RELCOMP;
                }
                else
                {
                  dseg->link_serv = 0xFE;
                  dseg->link_prot = 0xFE;
                  break;
                }
                m = hayes__readnum (dseg, &d, 10, 7);
                if (m != 0x2F)
                  d = 0xFFFFFFFEL;
                else if (d >= 1000000L)
                  d = 0xFFFFFFFDL;
              }
              break;
            case 3: /*els*/
              if (m == 0x2F)
              {
                y = 255;
                m = hayes__readstr (dseg, NULL, &y);
                if (m != 0x2F)
                  break;
                y = 255;
                m = hayes__readstr (dseg, NULL, &y);
                if (m == -1)
                  goto linkpar_raw;
                else if (m == 0x2F)
                {
                  m = hayes__readtok (dseg, hayes__conmsg3, \
                                      K_HAYES_CONMSG3_STRINGS);
                  if ((m == 0) || (m == 1))
                    goto linkpar_relcomp;
                  else if ((m == 2) || (m == 3))
                    goto linkpar_rel;
                  else
                    goto linkpar_error;
                }
              }
              break;
            case 4: /*usr*/
              if (m == 0x2F)
              {
                hayes__readtok (dseg, hayes__conmsg4x, 1);
                y = 255;
                m = hayes__readstr (dseg, NULL, &y);
                if (m != 0x2F)
                  break;
                m = hayes__readtok (dseg, hayes__conmsg4, \
                                    K_HAYES_CONMSG4_STRINGS);
                if (m == 0)
                {
                  linkpar_raw:
                  dseg->link_serv = K_HAYES_LINKSERVICE_DATA;
                  dseg->link_prot = K_HAYES_LINKPROTOCOL_RAW;
                }
                else if ((m == 1) || (m == 2))
                {
                  linkpar_relcomp:
                  dseg->link_serv = K_HAYES_LINKSERVICE_DATA;
                  dseg->link_prot = K_HAYES_LINKPROTOCOL_RELCOMP;
                }
                else if ((m == 3) || (m == 4))
                {
                  linkpar_rel:
                  dseg->link_serv = K_HAYES_LINKSERVICE_DATA;
                  dseg->link_prot = K_HAYES_LINKPROTOCOL_REL;
                }
                else
                {
                  linkpar_error:
                  dseg->link_serv = 0xFE;
                  dseg->link_prot = 0xFE;
                }
              }
              break;
          }
        }
        dseg->link_rate = d;
      }
    }
    w = 200;
    if (dseg->cfg.modem_type < K_HAYES_MODEMTYPE_GSM)
    {
      w = hayes__modeminfo_base[dseg->cfg.modem_type].tmline;
      w = (w << 3) + (w << 1);
    }
    if (w)
    {
      m = sio_txdelay (&(dseg->sio), term, dcd, ring, w);
      if (m < 0)
        n = m;
    }
    if ((n >= 0) && (ring && dseg->ring_event))
      n = E_HAYES_EVT_RINGING;
  }
  else
  {
    w = *timeout;
    w <<= 9;
    n = sio_txdelay (&(dseg->sio), term, dcd, ring, w);
    if (n < 0)
      return n;
    *timeout = 0;
    *vlines = 0;
    n = sio_rxerror (&(dseg->sio), term, dcd, ring, 1);
    if (n < 0)
      return n;
    n = sio_rxclear (&(dseg->sio), term, dcd, ring);
  }
  return n;
}

/*====*/
                                                            /* Get Hayes-Line */
static signed short hayes__getline (struct hayes_data *dseg,               \
                                    unsigned char term, unsigned char dcd, \
                                    unsigned char ring, unsigned char *timeout)
{
  unsigned char t,x,y,z;
  unsigned short w;
  signed short n,m;
  unsigned long d;

  /* Get line */
  w = 0;
  while (42)
  {
    x = 0;
    if (*timeout)
      y = 1;
    else
      y = 0;
    n = sio_getch (&(dseg->sio), term, dcd, ring, y, &x);
    if (n < 0)
      return n;
    if (n)
    {
      n = sio_rxerror (&(dseg->sio), term, dcd, ring, 1);
      if (n < 0)
        return n;
      if (n)
        return E_HAYES_GET_ERRDATA;
      if ((x != 0x0D) && (x != 0x0A))
      {
        if (w < K_HAYES_TEXTBUFWIDTH-1)
          dseg->textbuf[dseg->textlines][w++] = x;
      }
      else if (w)
        break;
    }
    else
    {
      if (w)
        return E_HAYES_GET_NOCHAR;
      else
      {
        if (*timeout == 0)
          return E_HAYES_GET_NOREPLY;
        (*timeout)--;
        if (*timeout == 0)
          return E_HAYES_GET_NOREPLY;
      }
    }
  }
  dseg->textbuf[dseg->textlines][w] = 0;
  dseg->textlen[dseg->textlines] = w;
strcpy (hayesevent, dseg->textbuf[dseg->textlines]);
wintop_notify (K_WINTOP_PARSUBSTATE);

  /* Check events */
  dseg->textposx = 0;
  dseg->textposy = dseg->textlines;
  n = hayes__readtok (dseg, hayes__event, K_HAYES_EVENT_STRINGS);
  if (n < 0)
    return 0;
  if (n < K_HAYES_EVENT_RIBACK)
    n++;

  /* Handle events */
  switch (n)
  {
    case K_HAYES_EVENT_CEER:             /* +CEER: */
      /* Error-Explanation */
      w = dseg->textlen[dseg->textposy] - dseg->textposx;
      if (w > K_HAYES_ERRORTXTLEN-1)
        w = K_HAYES_ERRORTXTLEN-1;
      if (w)
        memcpy (dseg->error_txtbuf, \
        &(dseg->textbuf[dseg->textposy][dseg->textposx]), w);
      dseg->error_txtbuf[w] = 0;
      dseg->error_txt = (unsigned char)w;
      break;
    case K_HAYES_EVENT_RING:             /* RING */
      dseg->ring_event = 1;
      break;
    case K_HAYES_EVENT_CIDNMBR:          /* NMBR */
      /* Caller-ID */
      x = 0;
      if (dseg->textposx < dseg->textlen[dseg->textposy])
      {
        y = dseg->textbuf[dseg->textposy][dseg->textposx];
        if ((y != 0x4F) && (y != 0x50))
        {
          x = K_HAYES_CLIPBUFLEN-1;
          m = hayes__readstr (dseg, dseg->ring_clipbuf, &x);
          if (m > 0)
            x = 0;
        }
      }
      dseg->ring_clip    = x;
      dseg->ring_clipfmt = 0xFF;
      dseg->ring_clipbuf[x] = 0;
      break;
    case K_HAYES_EVENT_CLIP:             /* +CLIP: */
      /* Clip-Number */
      dseg->ring_clipfmt = 0xFF;
      x = K_HAYES_CLIPBUFLEN-1;
      m = hayes__readstr (dseg, dseg->ring_clipbuf, &x);
      if ((m <= 0) || (m == 0x2C))
      {
        dseg->ring_clip = x;
        if (m <= 0)
          break;
      }
      else
      {
        dseg->ring_clip = 0;
        dseg->ring_clipbuf[0] = 0;
        break;
      }
      /* Clip-Format */
      m = hayes__readnum (dseg, &d, 10, 4);
      if ((m == -1) || (m == 0x2C))
      {
        if (d >= 0xF0L)
          dseg->ring_clipfmt = 0xFD;
        else
          dseg->ring_clipfmt = (unsigned char)d;
      }
      else
        dseg->ring_clipfmt = 0xFE;
      break;
    case K_HAYES_EVENT_CRING:            /* +CRING: */
      dseg->ring_event = 1;
      /*fallthrough*/
    case K_HAYES_EVENT_CR:               /* +CR: */
      /* Link-Service */
      m = hayes__readtok (dseg, hayes__report_cr, K_HAYES_REPORTCR_STRINGS);
      if (m < 0)
      {
        dseg->link_serv = 0xFE;
        dseg->link_prot = 0xFE;
      }
      else if (m < K_HAYES_REPORTCR_F)
      {
        dseg->link_serv = K_HAYES_LINKSERVICE_DATA;
        if (m < K_HAYES_REPORTCR_RA)
          dseg->link_prot = K_HAYES_LINKPROTOCOL_RAW;
        else
          dseg->link_prot = K_HAYES_LINKPROTOCOL_REL;
      }
      else if ((m == K_HAYES_REPORTCR_F) || (m == K_HAYES_REPORTCR_AFV))
      {
        dseg->link_serv  = K_HAYES_LINKSERVICE_FAX;
        dseg->link_prot = K_HAYES_LINKPROTOCOL_RAW;
      }
      else if (m < K_HAYES_REPORTCR_AAV)
      {
        dseg->link_serv  = K_HAYES_LINKSERVICE_VOICE;
        dseg->link_prot = K_HAYES_LINKPROTOCOL_RAW;
      }
      else
      {
        dseg->link_serv = K_HAYES_LINKSERVICE_DATA;
        if (m < K_HAYES_REPORTCR_ARAV)
          dseg->link_prot = K_HAYES_LINKPROTOCOL_RAW;
        else
          dseg->link_prot = K_HAYES_LINKPROTOCOL_REL;
      }
      break;
    case K_HAYES_EVENT_DR:               /* +DR: */
      /* Link-Compression */
      m = hayes__readtok (dseg, hayes__report_dr, K_HAYES_REPORTDR_STRINGS);
      if (m < 0)
        dseg->link_prot = 0xFE;
      else if (m > K_HAYES_REPORTDR_NONE)
        dseg->link_prot = K_HAYES_LINKPROTOCOL_RELCOMP;
      break;
    case K_HAYES_EVENT_CBC:              /* +CBC: */
      /* Main-Power-Flag */
      m = hayes__readnum (dseg, &d, 10, 2);
      if ((m == -1) || (m == 0x2C))
      {
        if (d > 1L)
          dseg->gsm_mainpwr = 0xFD;
        else
          dseg->gsm_mainpwr = (unsigned char)d;
        if (m == -1)
          break;
      }
      else
      {
        dseg->gsm_mainpwr = 0xFE;
        break;
      }
      /* Batterie-Level */
      m = hayes__readnum (dseg, &d, 10, 4);
      if ((m == -1) || (m == 0x2C))
      {
        if (d > 100L)
          dseg->gsm_battlvl = 0xFD;
        else
          dseg->gsm_battlvl = (unsigned char)d;
      }
      else
        dseg->gsm_battlvl = 0xFE;
      break;
    case K_HAYES_EVENT_SCKS:             /* ^SCKS: */
      /* Message-Flag */
      m = hayes__readnum (dseg, NULL, 10, 2);
      if (m != 0x2C)
        break;
      /* Sim-State */
      m = hayes__readnum (dseg, &d, 10, 2);
      if ((m == -1) || (m == 0x2C))
      {
        if (d > 1L)
          dseg->gsm_simstat = 0xFD;
        else
          dseg->gsm_simstat = (unsigned char)d;
      }
      else
        dseg->gsm_simstat = 0xFE;
      break;
    case K_HAYES_EVENT_SCID:             /* ^SCID: */
      x = K_HAYES_INFOLEN_SIMNR-1;
      hayes__readstr (dseg, dseg->infobuf_simnr, &x);
      dseg->info_simnr = x;
      break;
    case K_HAYES_EVENT_CREG:             /* +CREG: */
      /* Message-Flag */
      m = hayes__readnum (dseg, NULL, 10, 2);
      if (m != 0x2C)
        break;
      /* Login-State */
      m = hayes__readnum (dseg, &d, 10, 2);
      if ((m == -1) || (m == 0x2C))
      {
        if (d > 5L)
          dseg->gsm_netlogin = 0xFD;
        else
          dseg->gsm_netlogin = (unsigned char)d;
        if (m == -1)
          break;
      }
      else
      {
        dseg->gsm_netlogin = 0xFE;
        break;
      }
      /* LAC-Position */
      m = hayes__readnum (dseg, &d, 16, 5);
      if ((m == -1) || (m == 0x2C))
      {
        if (d >= 0xFFF0L)
          dseg->gsm_netlac = 0xFFFD;
        else
          dseg->gsm_netlac = (unsigned short)d;
        if (m == -1)
          break;
      }
      else
      {
        dseg->gsm_netlac = 0xFFFE;
        break;
      }
      /* CID-Position */
      m = hayes__readnum (dseg, &d, 16, 5);
      if ((m == -1) || (m == 0x2C))
      {
        if (d >= 0xFFF0L)
          dseg->gsm_netcid = 0xFFFD;
        else
          dseg->gsm_netcid = (unsigned short)d;
      }
      else
        dseg->gsm_netcid = 0xFFFE;
      break;
    case K_HAYES_EVENT_COPS:             /* +COPS: */
      if (dseg->textposx >= dseg->textlen[dseg->textposy])
        break;
      if (dseg->textbuf[dseg->textposy][dseg->textposx] != 0x28) /* ( */
      { /* Reply to AT+COPS? */
        /* Message-Flag */
        m = hayes__readnum (dseg, NULL, 10, 2);
        if (m != 0x2C)
          break;
        /* Data-Format */
        m = hayes__readnum (dseg, &d, 10, 2);
        if ((m != 0x2C) || (d != 2L))
          break;
        /* Network-Number */
        m = hayes__readnum (dseg, &d, 10, 6);
        if ((m == -1) || (m == 0x2C))
        {
          if (d >= 0xFFF0L)
            dseg->gsm_netnum = 0xFFFD;
          else
            dseg->gsm_netnum = (unsigned short)d;
        }
        else
          dseg->gsm_netnum = 0xFFFE;
      }
      else
      { /* Reply to AT+COPS=? */
        for (x=0; x<K_HAYES_NETLIST_LINES; x++)
        {
          dseg->netlist_state  [x] = 0xFF;
          dseg->netlist_netnum [x] = 0xFFFF;
          dseg->netlist_namelen[x] = 0;
          dseg->netlist_namebuf[x][0] = 0;
        }
        for (x=0; x<K_HAYES_NETLIST_LINES; x++)
        {
          m = hayes__readchar (dseg);
          if (m != 0x28)
            break;
          /* Network-State */
          m = hayes__readnum (dseg, &d, 10, 2);
          if ((m == -1) || (m == 0x2C))
          {
            if (d > 3L)
              dseg->netlist_state[x] = 0xFD;
            else
              dseg->netlist_state[x] = (unsigned char)d;
            if (m == -1)
              break;
          }
          else
          {
            dseg->netlist_state[x] = 0xFE;
            break;
          }
          /* Network-Name */
          y = K_HAYES_NETLIST_WIDTH-1;
          m = hayes__readstr (dseg, dseg->netlist_namebuf[x], &y);
          if ((m <= 0) || (m == 0x2C))
          {
            dseg->netlist_namelen[x] = y;
            if (m <= 0)
              break;
          }
          else
          {
            dseg->netlist_namelen[x] = 0;
            dseg->netlist_namebuf[x][0] = 0;
            break;
          }
          /* Dummy-String */
          y = 255;
          m = hayes__readstr (dseg, NULL, &y);
          if (m != 0x2C)
            break;
          /* Network-Number */
          m = hayes__readnum (dseg, &d, 10, 6);
          if ((m == -1) || (m == 0x29))
          {
            if (d >= 0xFFF0L)
              dseg->netlist_netnum[x] = 0xFFFD;
            else
              dseg->netlist_netnum[x] = (unsigned short)d;
            if (m == -1)
              break;
          }
          else
          {
            dseg->netlist_netnum[x] = 0xFFFE;
            break;
          }
          m = hayes__readchar (dseg);
          if (m != 0x2C)
            break;
        }
        /* Sort Netlist */
        for (x=0; x<K_HAYES_NETLIST_LINES-1; x++)
        {
          w = dseg->netlist_netnum[x];
          if (w >= 0xFFFE)
            break;
          y = x;
          for (z=x+1; z<K_HAYES_NETLIST_LINES; z++)
          {
            if (dseg->netlist_netnum[z] >= 0xFFFE)
              break;
            if (dseg->netlist_netnum[z] < w)
            {
              w = dseg->netlist_netnum[z];
              y = z;
            }
          }
          if (x != y)
          {
            t = dseg->netlist_state  [x];
            w = dseg->netlist_netnum [x];
            z = dseg->netlist_namelen[x];
            dseg->netlist_state  [x] = dseg->netlist_state  [y];
            dseg->netlist_netnum [x] = dseg->netlist_netnum [y];
            dseg->netlist_namelen[x] = dseg->netlist_namelen[y];
            dseg->netlist_state  [y] = t;
            dseg->netlist_netnum [y] = w;
            dseg->netlist_namelen[y] = z;
            for (z=0; z<K_HAYES_NETLIST_WIDTH; z++)
            {
              t = dseg->netlist_namebuf[x][z];
              dseg->netlist_namebuf[x][z] = dseg->netlist_namebuf[y][z];
              dseg->netlist_namebuf[y][z] = t;
            }
          }
        }
      }
      break;
    case K_HAYES_EVENT_CSQ:              /* +CSQ: */
      /* Signal-Level */
      m = hayes__readnum (dseg, &d, 10, 3);
      if ((m == -1) || (m == 0x2C))
      {
        if (d > 99L)
          dseg->gsm_rxsiglvl = 0xFD;
        else
          dseg->gsm_rxsiglvl = (unsigned char)d;
        if (m == -1)
          break;
      }
      else
      {
        dseg->gsm_rxsiglvl = 0xFE;
        break;
      }
      /* Signal-Quality */
      m = hayes__readnum (dseg, &d, 10, 3);
      if ((m == -1) || (m == 0x2C))
      {
        if (d > 99L)
          dseg->gsm_rxsigqual = 0xFD;
        else
          dseg->gsm_rxsigqual = (unsigned char)d;
      }
      else
        dseg->gsm_rxsigqual = 0xFE;
      break;
    case K_HAYES_EVENT_SMGO:             /* ^SMGO: */
      /* Message-Flag */
      m = hayes__readnum (dseg, NULL, 10, 2);
      if (m != 0x2C)
        break;
      /* Sms-State */
      m = hayes__readnum (dseg, &d, 10, 2);
      if ((m == -1) || (m == 0x2C))
      {
        if (d > 2L)
          dseg->sms_overflow = 0xFD;
        else
          dseg->sms_overflow = (unsigned char)d;
      }
      else
        dseg->sms_overflow = 0xFE;
      break;
  }
  return n;
}

/*----*/
                                                    /* Send Command-Character */
static signed short hayes__putch (struct hayes_data *dseg, unsigned char term, \
                                  unsigned char dcd, unsigned char ring,       \
                                  unsigned char echo, unsigned char data)
{
  unsigned char x;
  signed short n;

  x = 10;
  if (dseg->cfg.modem_type < K_HAYES_MODEMTYPE_GSM)
    x = hayes__modeminfo_base[dseg->cfg.modem_type].tmchar;
  n = sio_putch (&(dseg->sio), term, dcd, ring, x, data);
  if (n < 0)
    return n;
  n = sio_flush (&(dseg->sio), term, dcd, ring, 1);
  if (n < 0)
    return n;
  if (echo)
  {
    x = 0;
    n = sio_getch (&(dseg->sio), term, dcd, ring, 1, &x);
    if (n < 0)
      return n;
    if (!n)
      return E_HAYES_PUT_NOECHO;
    n = sio_rxerror (&(dseg->sio), term, dcd, ring, 1);
    if (n < 0)
      return n;
    if (n)
      return E_HAYES_PUT_ERRECHO;
    if (x != data)
      return E_HAYES_PUT_BADECHO;
  }
  return 0;
}

/*====*/
                                                                /* Read Token */
static signed short hayes__readtok (struct hayes_data *dseg,        \
                                    const unsigned char **textlist, \
                                    unsigned char texts)
{
  const unsigned char *p,*q;
  unsigned char c,x,y;

  /* Find reply */
  for (x=0; x<texts; x++)
  {
    p = &(dseg->textbuf[dseg->textposy][dseg->textposx]);
    q = textlist[x];
    y = 0;
    while (42)
    {
      c = *p++;
      if ((c >= 0x61) && (c <= 0x7A))
        c &= 0xDF;
      if (c != *q++)
        break;
      y++;
      if (!(*q))
      {
        dseg->textposx += y;
        return x;
      }
    }
  }
  return -1;
}

/*--*/
                                                               /* Read String */
static signed short hayes__readstr (struct hayes_data *dseg, \
                                    unsigned char *text, unsigned char *len)
{
  unsigned char a,x;
  signed short n;

  a = 0;
  x = 0;
  while (42)
  {
    n = hayes__readchar (dseg);
    if (n == 0x22)
    {
      a ^= 1;
      continue;
    }
    if ((n < 0) || (!a && ((n == 0x09) || (n == 0x20) || (n == 0x2C) \
                        || (n == 0x2F))))
      break;
    if (x >= *len)
    {
      n = 0;
      break;
    }
    if (text != NULL)
      *text++ = (unsigned char)n;
    x++;
  }
  if (text != NULL)
    *text = 0;
  *len = x;
  return n;
}

/*--*/
                                                               /* Read Number */
static signed short hayes__readnum (struct hayes_data *dseg,                \
                                    unsigned long *val, unsigned char base, \
                                    unsigned char digits)
{
  unsigned char a,d;
  unsigned long x;
  signed short n;

  a = 0;
  d = 0;
  x = 0L;
  n = 0;
  while (d < digits)
  {
    n = hayes__readchar (dseg);
    if (n == 0x22)
    {
      a ^= 1;
      continue;
    }
    if ((n < 0) || (!a && ((n == 0x09) || (n == 0x20) || (n == 0x2C) \
                        || (n == 0x29) || (n == 0x2F))))
      break;
    if ((n >= 0x30) && (n <= 0x39))
      n -= 0x30;
    else if ((n >= 0x41) && (n <= 0x5A))
      n -= 0x37;
    else if ((n >= 0x61) && (n <= 0x7A))
      n -= 0x57;
    else
      return -2;
    if (n >= base)
      return -2;
    x *= base;
    x += n;
    n = 0;
    d++;
  }
  if (val != NULL)
    *val = x;
  return n;
}

/*--*/
                                                            /* Read Character */
static signed short hayes__readchar (struct hayes_data *dseg)
{
  signed short n;

  if (dseg->textposx >= dseg->textlen[dseg->textposy])
    return -1;
  n = dseg->textbuf[dseg->textposy][dseg->textposx++];
  return n;
}

/*----*/
                                                              /* Write String */
static signed short hayes__writestr (struct hayes_data *dseg, \
                                     const unsigned char *text)
{
  signed short n;

  while (*text)
  {
    n = hayes__writechar (dseg, *text++);
    if (n < 0)
      return n;
  }
  return 0;
}

/*--*/
                                                              /* Write Number */
static signed short hayes__writenum (struct hayes_data *dseg,               \
                                     unsigned long val, unsigned char base, \
                                     unsigned char digits)
{
  unsigned char chrbuf[32];
  unsigned char x,y,z;
  signed short n;

  /* Convert */
  x = 0;
  for (y=0; y<32; y++)
  {
    z = (unsigned char)(val % base);
    val /= base;
    if (z)
      x = y + 1;
    if (z < 10)
      z += 0x30;
    else if (z < 36)
      z += 0x37;
    else
      z = 0x3F; /* ? */
    chrbuf[31-y] = z;
  }

  /* Print */
  if (x < digits)
    x = digits;
  for (y=0; y<x; y++)
  {
    n = hayes__writechar (dseg, chrbuf[32-x+y]);
    if (n < 0)
      return n;
  }
  return 0;
}

/*--*/
                                                           /* Write Character */
static signed short hayes__writechar (struct hayes_data *dseg, \
                                      unsigned char data)
{
  if (dseg->textposx >= K_HAYES_TEXTBUFWIDTH)
    return -1;
  if (dseg->textposx == K_HAYES_TEXTBUFWIDTH-1)
    data = 0;
  dseg->textbuf[0][dseg->textposx++] = data;
  return 0;
}


/*** EOF ***/
